﻿using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows.Data;
using System.Windows.Input;
using Caliburn.Micro;
using CardPlay.Client.Converters;
using CardPlay.Client.Model;
using CardPlay.Client.Tasks;
using CardPlay.Model;
using CardPlay.Model.GameModel;
using CardPlay.Model.Primitives;

namespace CardPlay.Client.ViewModels
{
    [Export(typeof(GameBrowserViewModel)), PartCreationPolicy(CreationPolicy.NonShared)]
    public sealed class GameBrowserViewModel : Screen, IDialog
    {
        private readonly CollectionViewSource _dataView = new CollectionViewSource();
        private readonly GroupingAction<IGameDescriptor>[] _groupingActions;

        private GameHistoryViewModel _history;
        private GroupingAction<IGameDescriptor> _selectedGrouping;
        private string _searchText = String.Empty;
        private int _gameNumber = 1;

        public event EventHandler<DialogResultCompletionEventArgs> Completed;

        public GameBrowserViewModel()
        {
            DisplayName = Resources.Caption_GameBrowser;

            ActivateItem = new RelayCommand<IGameDescriptor>(delegate { OpenItem(); }, delegate { return CanOpenItem; });
            RandomGameNumber = new RelayCommand<IGameDescriptor>(delegate
            {
                    RandomSeries.Seed = (int)DateTime.Now.Ticks;
                    GameNumber = RandomSeries.Next(int.MaxValue);
            });

            var list = GameRepository.Games.ToArray();

            TotalGames = list.Length;

            _dataView.Source = list;
            _dataView.Filter += OnFilterList;

            _groupingActions = new[]
            {
                new GroupingAction<IGameDescriptor>("DisplayName", "None"), 
                null,
                new GroupingAction<IGameDescriptor>("DisplayName", "Names") 
                    { GenerateGroups = (descriptor, type) => descriptor.DisplayName[0] },
                new GroupingAction<IGameDescriptor>("Family", "Family") 
                    { GenerateGroups = (descriptor, type) => descriptor.Family },
                null,
                new GroupingAction<IGameDescriptor>("NumberOfDecks", "Number of Decks") 
                    { GenerateGroups = GroupByNumberOfDecks },
                new GroupingAction<IGameDescriptor>("LevelOfSkill", "Level of Skill") 
                    { GenerateGroups = (descriptor, type) => descriptor.LevelOfSkill.ToLocalizedString() },
                new GroupingAction<IGameDescriptor>("LevelOfDifficulty", "Level of Difficulty") 
                    { GenerateGroups = (descriptor, type) => descriptor.LevelOfDifficulty.ToLocalizedString() },
                new GroupingAction<IGameDescriptor>("TypicalPlayingTime", "Game Time") 
                    { GenerateGroups = (descriptor, type) => descriptor.TypicalPlayingTime.ToLocalizedString() }
            };

            SelectedGrouping = _groupingActions[0];
        }

        public ICommand ActivateItem { get; private set; }

        public ICommand RandomGameNumber { get; private set; }

        [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
        public IRandomNumberSequence RandomSeries { get; set; }

        [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
        public GamePreviewViewModel Preview { get; set; }

        [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
        public GameHistoryViewModel History
        {
            get { return _history; }
            set
            {
                if (_history != null) _history.RetryGame = null;
                _history = value;
                if (_history != null) _history.RetryGame = new RelayCommand<RecordOfPlay>(play => { GameNumber = play.GameNumber; OpenItem(); });
            }
        }

        public int TotalGames
        {
            get; private set;
        }

        public int GameNumber
        {
            get { return _gameNumber; }
            set
            {
                _gameNumber = value;
                NotifyOfPropertyChange(() => GameNumber);
            }
        }

        public IGameDescriptor SelectedGame
        {
            get { return Preview.SelectedGame; }

            set
            {
                if (value == SelectedGame) return;

#if ! SILVERLIGHT
                History.Source = value != null ? GameRepository.History.HistoryOfPlayForGame(value.Id) : null;
#endif
                Preview.SelectedGame = value;

                NotifyOfPropertyChange(() => SelectedGame);
                NotifyOfPropertyChange(() => CanOpenItem);
                NotifyOfPropertyChange(() => History);
            }
        }

        public object GameCollection
        {
            get { return _dataView.View; }
        }

        public object GroupingActions
        {
            get { return _groupingActions; }
        }

        public object SelectedGrouping
        {
            get { return _selectedGrouping; }
            set
            {
                var action = value as GroupingAction<IGameDescriptor>;
                if (action == null) return;

                _selectedGrouping = action;
                action.Apply(_dataView);

                NotifyOfPropertyChange(() => SelectedGrouping);
            }
        }

        public string SearchText
        {
            get { return _searchText; }
            set
            {
                if (value == SearchText) return;

                _searchText = value;
                _dataView.View.Refresh();

                NotifyOfPropertyChange(() => SearchText);
            }
        }

        public bool CanOpenItem
        {
            get { return SelectedGame != null; }
        }

        public void OpenItem()
        {
            Completed(this, new DialogResultCompletionEventArgs { DialogResult = CanOpenItem } );
        }

        private void OnFilterList(object sender, FilterEventArgs e)
        {
            var item = e.Item as IGameDescriptor;
            e.Accepted = item == null || String.IsNullOrEmpty(SearchText) || item.DisplayName.StartsWith(SearchText, StringComparison.CurrentCultureIgnoreCase);
        }

        private static object GroupByNumberOfDecks(IGameDescriptor descriptor, Type targetType)
        {
            return descriptor.NumberOfDecks == 1 ? "1 deck" : (descriptor.NumberOfDecks + " decks");
        }
    }
}
